import java.util.Scanner;

/**
 * This program counts the number of prime integers between 3000001 and 6000000.
 * The work is divided among one to five threads. The number of threads is
 * chosen by the user.
 */
public class ThreadTest2 {

	/**
	 * The starting point for the range of integers that are tested for
	 * primality. The range is from (start+1) to (2*start). Note the value of
	 * start is chosen to be divisible by 2, 3, 4, and 5 to make it easy to
	 * divide up the range among the threads.
	 */
	private static final int START = 3000000;

	/**
	 * The total number of primes found. Each thread counts the number of primes
	 * in a different range of integers. After it finishes counting, it adds its
	 * count to the total.
	 */
	private static int total;

	private static Scanner sc;

	/**
	 * Adds x to total. This method is synchronized so that it can be safely
	 * used by several different threads.
	 */
	synchronized private static void addToTotal(int x) {
		total = total + x;
		System.out.println(total + " primes found so far.");
	}

	/**
	 * A Thread belonging to this class will count primes in a specified range
	 * of integers. The range is from min to max, inclusive, where min and max
	 * are given as parameters to the constructor. After counting, the thread
	 * outputs a message about the number of primes that it has found, and it
	 * adds its count to the overall total by calling the addToTotal(int)
	 * method.
	 */
	private static class CountPrimesThread extends Thread {
		int count = 0;
		int min, max;

		public CountPrimesThread(int min, int max) {
			this.min = min;
			this.max = max;
		}

		public void run() {
			count = countPrimes(min, max);
			System.out.println("There are " + count + " primes between " + min
					+ " and " + max);
			addToTotal(count);
		}
	}

	/**
	 * Counts the primes in the range from (START+1) to (2*START), using a
	 * specified number of threads. The total elapsed time is printed.
	 * 
	 * @param numberOfThreads
	 */
	private static void countPrimesWithThreads(int numberOfThreads) {
		int increment = START / numberOfThreads;
		System.out.println("\nCounting primes between " + (START + 1) + " and "
				+ (2 * START) + " using " + numberOfThreads + " threads...\n");
		long startTime = System.currentTimeMillis();
		CountPrimesThread[] worker = new CountPrimesThread[numberOfThreads];
		for (int i = 0; i < numberOfThreads; i++)
			worker[i] = new CountPrimesThread(START + i * increment + 1, START
					+ (i + 1) * increment);
		total = 0;
		for (int i = 0; i < numberOfThreads; i++)
			worker[i].start();
		for (int i = 0; i < numberOfThreads; i++) {
			while (worker[i].isAlive()) {
				try {
					worker[i].join();
				} catch (InterruptedException e) {
				}
			}
		}
		long elapsedTime = System.currentTimeMillis() - startTime;
		System.out.println("\nThe number of primes is " + total + ".");
		System.out.println("\nTotal elapsed time:  " + (elapsedTime / 1000.0)
				+ " seconds.\n");
	}

	/**
	 * Gets the number of threads from the user and counts primes using that
	 * many threads.
	 */
	public static void main(String[] args) {
		sc = new Scanner(System.in);
		int processors = Runtime.getRuntime().availableProcessors();
		if (processors == 1)
			System.out
					.println("Your computer has only 1 available processor.\n");
		else
			System.out.println("Your computer has " + processors
					+ " available processors.\n");
		int numberOfThreads = 0;
		while (numberOfThreads < 1 || numberOfThreads > 5) {
			System.out
					.print("How many threads do you want to use  (from 1 to 5) ?  ");
			numberOfThreads = sc.nextInt();
			if (numberOfThreads < 1 || numberOfThreads > 5)
				System.out.println("Please enter 1, 2, 3, 4, or 5 !");
		}
		countPrimesWithThreads(numberOfThreads);
	}

	/**
	 * Count the primes between min and max, inclusive.
	 */
	private static int countPrimes(int min, int max) {
		int count = 0;
		for (int i = min; i <= max; i++)
			if (isPrime(i))
				count++;
		return count;
	}

	/**
	 * Test whether x is a prime number. x is assumed to be greater than 1.
	 */
	private static boolean isPrime(int x) {
		int top = (int) Math.sqrt(x);
		for (int i = 2; i <= top; i++)
			if (x % i == 0)
				return false;
		return true;
	}

}
